﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls.Base;
using Silvermoon.Visuals;
using Silvermoon.Images;
using System.Drawing;
using Silvermoon.Utils;
using Silvermoon.OpenGL;
using Silvermoon.Controls.Classes;
using Silvermoon.Animations;
using Silvermoon.Controls.Utils;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Button control that shows a drop down menu on demand to select an item.
    /// See also <seealso cref="T:DropDownMenu"/>.
    /// </summary>
    public class DropDownButton : ButtonBase, IScrollEventBehavior
    {
        private DropDownMenu menu;
        private BackgroundVisual background;
        private StringVisual visualText;
        private RectangleVisual separatorLine;
        private ImageVisual downImage;
        private Color color;
        private string text = string.Empty;
        private Alignment alignment = Alignment.Near;
        private bool showDropDown;

        private static readonly Transformation[] flipTransformation = new Transformation[] { new RotateTransform { Angle = RotateTransform.Degree180, Axis = RotateAxis.XAxis } };


        public DropDownButton()
            : base()
        {
            menu = new DropDownMenu();
            background = new BackgroundVisual { Texture = SysImages.Button };
            visualText = new StringVisual { LineAlignment = Alignment.Center, Alignment = Alignment.Near, LineMode = LineMode.SingleLineEndElipsis };
            separatorLine = new RectangleVisual { Color = ColorUtil.AlphaColor(Color.Black, 128) };
            downImage = new ImageVisual { Image = SysImages.DownImage, Alpha = 128, Color = Color.White };
            Visuals = new Visual[] { background, separatorLine, downImage, visualText };
            TextColor = Color.White;
            menu.MenuPosition = MenuPosition.Left;
            base.Toggle = true;
            menu.Hidden += new EventHandler(OnHidden);
            menu.ItemClicked += new EventHandler<ItemEventArgs>(OnItemClicked);
            Color = SysColors.ButtonColor;
        }


        /// <summary>
        /// Gets or sets how to align the button text.
        /// </summary>
        public Alignment Alignment
        {
            get { return alignment; }
            set
            {
                if (alignment != value)
                {
                    alignment = value;
                    Invalidate(InvalidationFlag.Layout);
                }
            }
        }

        public override bool Toggle { get { return base.Toggle; } set { } }

        public override void Dispose()
        {
            visualText.Dispose();
            separatorLine.Dispose();
            downImage.Dispose();

            background.Dispose();
            Visuals = null;
            menu.Dispose();
            base.Dispose();
        }

        public string Text
        {
            get { return text; }
            set
            {
                if (text != value) { text = value; Invalidate(InvalidationFlag.Text); }
            }
        }

        public Color Color
        {
            get { return color; }
            set
            {
                if (color != value) { color = value; Invalidate(InvalidationFlag.Color); }
            }
        }

        public bool ShowDropDown
        {
            get { return showDropDown; }
            set
            {
                if (!Enabled) value = false;
                IsDown = value;
                if (showDropDown != value)
                {
                    showDropDown = value;
                    OnShowDropDownChanged();
                }
            }
        }

        public IList<ItemBase> Items { get { return menu.Items; } }

        public MenuItem AddItem(string text)
        {
            MenuItem item = new MenuItem { Text = text };
            Items.Add(item);
            return item;
        }

        public void AddItems(params string[] text)
        {
            foreach (string s in text)
            {
                MenuItem item = new MenuItem { Text = s };
                Items.Add(item);
            }
        }

        public ItemSeparator AddSeparator()
        {
            ItemSeparator separator = new ItemSeparator();
            Items.Add(separator);
            return separator;
        }

        protected virtual void OnItemClicked(object sender, ItemEventArgs e)
        {
            if (ItemClicked != null) ItemClicked(this, e);
            MenuItem item = e.Item as MenuItem;
            if (item != null)
            {
                Text = item.Text;
            }
        }

        protected virtual void OnHidden(object sender, EventArgs e)
        {
            IsDown = false;
        }

        protected override void OnLayout()
        {
            int dy = IsDownOrPressed ? 1 : 0;

            if (CheckFlag(InvalidationFlag.Size))
            {
                background.Size = Size;
                int w = Size.Width;
                int h = Size.Height;
                menu.Width = w;
                downImage.Bounds = Control.ScaledRect(w - 32 + 2, (h - 16) / 2, 16, 16);
                separatorLine.Bounds = Control.ScaledRect(w - 40, 12, 2, h - 24);
                visualText.Bounds = Control.ScaledRect(16, dy + 2, w - 40 - 16, h - 4);
                visualText.Color = Enabled ? TextColor : ColorUtil.AlphaBlendColor(color, SysColors.ButtonTextColor, 128);


            }

            if (CheckFlag(InvalidationFlag.Text | InvalidationFlag.Appearance))
            {
                visualText.Text = text;
            }

            if (CheckFlag(InvalidationFlag.Color | InvalidationFlag.Enabled))
            {
                Color c = Enabled ? SysColors.ButtonTextColor : ColorUtil.AlphaBlendColor(color, SysColors.ButtonTextColor, 128);
                separatorLine.Color = c;
                background.Colors = ColorUtil.CreatePlasticColors(color);
                downImage.Alpha = Enabled ? 128 : 48;
            }

            if (CheckFlag(InvalidationFlag.LayoutAppearance | InvalidationFlag.Enabled))
            {
                int w = Size.Width;
                int h = Size.Height;
                SetBgColor();
                visualText.Bounds = Control.ScaledRect(16, dy + 2, w - 40 - 16, h - 4);
                downImage.Image = !IsDown ? SysImages.DownImage : SysImages.UpImage;
                visualText.Alignment = alignment;
            }

            base.OnLayout();
        }

        private void SetBgColor()
        {
            Color color = this.color;
            background.Colors = ColorUtil.CreatePlasticColors(color);
            background.Transformations = IsDownOrPressed | !Enabled ? flipTransformation : null;
            background.Invalidate();
        }

        protected override void OnDownChanged()
        {
            ShowDropDown = IsDown;
            base.OnDownChanged();
        }





        protected virtual void OnShowDropDownChanged()
        {
            IsDown = ShowDropDown;
            if (ShowDropDown)
            {
                menu.Show(this);
                // fx looks better without fading, since due to acceleration the menu is almost transparent when you notice the collapse:
                menu.HideTransition = ContextMenuUtil.GetReverseTransitionMode(menu, this) & ~TransitionMode.Fade;
            }
            else menu.Hide();
        }


        protected override void OnEnabledChanged()
        {
            if (!Enabled) ShowDropDown = false;
            base.OnEnabledChanged();
        }

        protected override void OnVisibleChanged()
        {
            if (!Visible && menu.Visible) ShowDropDown = false;
            base.OnVisibleChanged();
        }


        public event EventHandler<ItemEventArgs> ItemClicked;

        #region IScrollEventBehavior Members

        public void OnScroll(ScrollEventArgs e)
        {
            ShowDropDown = false;
        }

        public void OnBeginScrollData(BeginScrollEventArgs e)
        {
        }

        public void OnEndScrollData(EndScrollEventArgs e)
        {
        }

        #endregion
    }
}
